﻿using SCG.SolarVengeanceEngine;
using SCG.TurboSprite;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using SCG.WinForms;
using SCG.SolarVengeanceEngine.Orders;
using SCG.General;
using SCG.SolarVengeanceEngine.StarShips;

namespace SCG.SolarVengeance
{
    public class StarShipSprite : Sprite
    {
        //constructor
        public StarShipSprite(StarShip ship)
            : base()
        {
            StarShip = ship;
            Tag = ship;
            Shape = new Rectangle(-Constants.CellHalf, -Constants.CellHalf, Constants.CellSize, Constants.CellSize);
        }

        //is it selected?
        public bool Selected { get; set; }

        //access the image
        private Image _image = null;
        public Image Image
        {
            get
            {
                return _image;
            }
        }

        //Dynamically adjust the visible bounds based on scanners and order destinations
        public override void Process()
        {
            if (StarShip.OwnedByCurrentPlayer)
            {
                if (StarShip.DrawVisibleRange && StarShip.Value > 1)
                {
                    int radius = StarShip.Value * Constants.CellSize;
                    Shape = new RectangleF(-radius, -radius, radius * 2 + Constants.CellSize, radius * 2 + Constants.CellSize);
                }

                _x1 = Int32.MaxValue;
                _y1 = Int32.MaxValue;
                _x2 = Int32.MinValue;
                _y2 = Int32.MinValue;

                //account for remote view
                if (StarShip.ShipType == StarShipType.RemoteViewer)
                {
                    RemoteViewer rv = StarShip as RemoteViewer;
                    if (rv.ViewX != -1)
                    {
                        SCGPoint view = new SCGPoint(rv.ViewX - StarShip.Value, rv.ViewY - StarShip.Value);
                        ProcessOrderPoint(view);
                        view.Offset(StarShip.Value * 2 + 1, StarShip.Value * 2 + 1);
                        ProcessOrderPoint(view);
                    }
                }

                //create a rectangle that encompasses the order line
                if (_x1 != Int32.MaxValue || (StarShip.Order != null && StarShip.Order.LineColor != SCGColor.Transparent))
                {
                    //calculate based on all points in patrol path
                    StarShipOrder o = StarShip.Order;
                    if (o != null)
                    {
                        if (o is PatrolOrder)
                        {
                            PatrolOrder po = o as PatrolOrder;
                            foreach (SCGPoint pt in po.PatrolPoints)
                                ProcessOrderPoint(pt);
                        }
                        else if (o is TransportOrder)
                        {
                            TransportOrder to = o as TransportOrder;
                            foreach (SVMapObject svm in to.Sources)
                                ProcessOrderPoint(svm.Location);
                            if (to.MapObject != null)
                                ProcessOrderPoint(to.MapObject.Location);
                        }
                        else if (o is PointOrder)
                        {
                            PointOrder po = o as PointOrder;
                            SCGPoint dest = po.TargetPoint;
                            ProcessOrderPoint(dest);
                        }
                        else
                            ProcessOrderPoint(o.TargetPoint);
                    }
                    if (_x1 != Int32.MaxValue)
                    {
                        int w = _x2 - _x1;
                        int h = _y2 - _y1;
                        int x = _x1 - StarShip.X;
                        int y = _y1 - StarShip.Y;
                        Rectangle rectOrder = new Rectangle(x * Constants.CellSize, y * Constants.CellSize, w * Constants.CellSize + Constants.CellSize, h * Constants.CellSize + Constants.CellSize);
                        Shape = RectangleF.Union(Shape, rectOrder);
                    }
                }
                ClickShape = new Rectangle(-Constants.CellHalf, -Constants.CellHalf, Constants.CellSize, Constants.CellSize);
            }
        }

        //Adjust bounds of order line rectangle
        private void ProcessOrderPoint(SCGPoint pt)
        {
            if (pt.X < _x1)
                _x1 = pt.X;
            if (pt.X > _x2)
                _x2 = pt.X;
            if (pt.Y < _y1)
                _y1 = pt.Y;
            if (pt.Y > _y2)
                _y2 = pt.Y;
        }

        //render
        public override void Render(Graphics g)
        {
            //Visible?
            if (!StarShip.VisibleToCurrentPlayer)
                return;

            //Where to draw
            int xDraw = (int)(X - Surface.OffsetX);
            int yDraw = (int)(Y - Surface.OffsetY);

            //Draw cloaking effect?
            if (StarShip.DiscoveredByCurrentPlayer || StarShip.OwnedByCurrentPlayer)
                if (StarShip.Cloaked)
                {
                    int cx1 = (int)(X - Surface.OffsetX + SVGame.RNG.Next(5)) - Constants.CellHalf;
                    int cy1 = (int)(Y - Surface.OffsetY + SVGame.RNG.Next(5)) - Constants.CellHalf;
                    int cx2 = (int)(X - Surface.OffsetX + Constants.CellHalf - 4 + SVGame.RNG.Next(5) - 2);
                    int cy2 = (int)(Y - Surface.OffsetY + Constants.CellHalf - 4 + SVGame.RNG.Next(5) - 2);
                    g.DrawEllipse(Pens.Silver, cx1, cy1, cx2 - cx1, cy2 - cy1);
                }

            //Draw pulsing of CloakDetectors
            if (StarShip.OwnedByCurrentPlayer && StarShip.ShipType == StarShipType.CloakDetector)
            {
                CloakDetector cd = StarShip as CloakDetector;
                if (cd.CloakArrows.Count > 0)
                {
                    Pen pulse = new Pen(Color.Black);
                    for (int pulses = 0; pulses < 8; pulses++)
                    {
                        int cx1 = (int)(X - Surface.OffsetX + SVGame.RNG.Next(5) - 4 - Constants.CellHalf);
                        int cy1 = (int)(Y - Surface.OffsetY + SVGame.RNG.Next(5) - 4 - Constants.CellHalf);
                        int cx2 = (int)(X - Surface.OffsetX + Constants.CellHalf + SVGame.RNG.Next(5) - 2);
                        int cy2 = (int)(Y - Surface.OffsetY + Constants.CellHalf + SVGame.RNG.Next(5) - 2);
                        pulse.Color = Sprite.ColorFromRange(Color.FromArgb(20, Color.DarkMagenta), Color.FromArgb(200, 255, 213, 67));
                        g.DrawEllipse(pulse, cx1, cy1, cx2 - cx1, cy2 - cy1);
                    }
                }
            }

            //obtain and render image, wobble if damaged
            Bitmap bmp;
            if (StarShip.DiscoveredByCurrentPlayer)
                bmp = StarShipImageFactory.GetImage(StarShip.ShipType, StarShip.Owner.Color.ToColor(), StarShip.Facing);
            else
                bmp = StarShipImageFactory.GetUnknown(StarShip.Owner.Color.ToColor());

            int sx = 0;
            int sy = 0;
            int damageFactor = (int)((100.0d - StarShip.Health) / 20.0d);
            if (damageFactor > 0)
            {
                sx += SVGame.RNG.Next(damageFactor) - damageFactor / 2;
                sy += SVGame.RNG.Next(damageFactor) - damageFactor / 2;
            }

            g.DrawImage(bmp, xDraw - bmp.Width / 2 + sx, yDraw - bmp.Height / 2 + sy);
            _image = bmp;

            //Draw tug lines
            if (StarShip is Tug)
            {
                Tug tug = StarShip as Tug;
                if (tug.Towing != null)
                    if (tug.VisibleToCurrentPlayer)
                        if (tug.OwnedByCurrentPlayer || tug.Towing.OwnedByCurrentPlayer)
                            DrawRays(g, tug, tug.Towing, Color.FromArgb(40, Color.Purple), Color.FromArgb(200, Color.White));
            }

            //Draw ShieldGenerator rays
            if (StarShip.OwnedByCurrentPlayer && StarShip.ShipType == StarShipType.ShieldGenerator)
                if (StarShip.Order != null && StarShip.Order.OrderType == OrderType.TargetStarSystem)
                {
                    MapObjectOrder moo = StarShip.Order as MapObjectOrder;
                    if (StarShip.IsAdjacent(moo.MapObject))
                        DrawRays(g, StarShip, moo.MapObject, Color.FromArgb(0, Color.Black), Color.FromArgb(200, Color.OliveDrab));
                }

            //Draw the drag lines for GravShips
            if (StarShip.VisibleToCurrentPlayer && StarShip.ShipType == StarShipType.GravShip)
            {
                GravShip gs = StarShip as GravShip;
                if (gs.Dragging != null)
                {
                    bool showRays = gs.OwnedByCurrentPlayer;
                    if (!showRays)
                        if (gs.Dragging is StarSystem)
                        {
                            StarSystem sys = gs.Dragging as StarSystem;
                            showRays = sys.OwnedByCurrentPlayer;
                        }
                    if (showRays)
                        DrawRays(g, gs, gs.Dragging, Color.FromArgb(40, Color.Orange), Color.FromArgb(200, Color.White));
                }
            }

            //Draw beam
            if (StarShip.VisibleToCurrentPlayer && StarShip.ShipType == StarShipType.BeamShip)
            {
                BeamShip bs = StarShip as BeamShip;
                if (bs.BeamTarget != null)
                {
                    int bx1 = 0;
                    int by1 = 0;
                    int bx2 = 0;
                    int by2 = 0;
                    Sprite sprite = (Sprite)StarShip.Tag;
                    bx1 = (int)(sprite.X - Surface.OffsetX);
                    by1 = (int)(sprite.Y - Surface.OffsetY);
                    Sprite targetSprite = (Sprite)bs.BeamTarget.Tag;
                    bx2 = (int)(targetSprite.X - Surface.OffsetX);
                    by2 = (int)(targetSprite.Y - Surface.OffsetY);
                    Pen beamPen = new Pen(Color.Red, 3);
                    for (int beam = 0; beam < 5; beam++)
                    {
                        beamPen.Color = Sprite.ColorFromRange(Color.FromArgb(30, Color.Black), Color.FromArgb(128, Color.Red));
                        g.DrawLine(beamPen, bx1 + RND.Next(5) - 2, by1 + RND.Next(5) - 2, bx2 + RND.Next(5) - 2, by2 + RND.Next(5) - 2);
                    }
                }
            }

            //Render value/engines
            if (StarShip.DiscoveredByCurrentPlayer)
            {
                //engine mark
                Brush br = Brushes.White;
                if (StarShip.Engines < StarShip.MaxEngines / 2)
                    br = Brushes.Red;
                else if (StarShip.Engines < StarShip.MaxEngines)
                    br = Brushes.Yellow;
                DrawLabel(g, StarShip.EngineMark, ValuesFont, br, X - Surface.OffsetX - Constants.CellHalf, Y - Surface.OffsetY - Constants.CellHalf);

                //value mark
                if (StarShip.PrimarySystemMark != "")
                {
                    br = Brushes.White;
                    if (StarShip.Value < StarShip.MaxValue / 2)
                        br = Brushes.Red;
                    else if (StarShip.Value < StarShip.MaxValue)
                        br = Brushes.Yellow;
                    DrawLabel(g, StarShip.PrimarySystemMark, ValuesFont, br, X - Surface.OffsetX - Constants.CellHalf, Y - Surface.OffsetY + 36 - Constants.CellHalf);
                }
            }
            else
            {
                //draw known waepons rating of unidentified ships
                if (StarShip.PrimarySystemMark != "")
                {
                    DrawLabel(g, StarShip.PrimarySystemMark, ValuesFont, Brushes.White, X - Surface.OffsetX - Constants.CellHalf, Y - Surface.OffsetY + 36 - Constants.CellHalf);
                }
            }

            //Render selection box
            if (Selected)
                g.DrawRectangle(Pens.Gray, X - Surface.OffsetX + 2 - Constants.CellHalf, Y - Surface.OffsetY + 2 - Constants.CellHalf, Constants.CellSize - 4, Constants.CellSize - 4);

            //Render type-specific effects
            if (StarShip.OwnedByCurrentPlayer)
            {
                //Draw visible ranges
                if (StarShip.DrawVisibleRange && StarShip.Value > 1)
                {
                    int radius = StarShip.Value * Constants.CellSize + Constants.CellHalf;
                    float drawX = X;
                    float drawY = Y;
                    if (StarShip.ShipType == StarShipType.RemoteViewer)
                    {
                        RemoteViewer rv = StarShip as RemoteViewer;
                        drawX = rv.ViewX * Constants.CellSize + Constants.CellHalf;
                        drawY = rv.ViewY * Constants.CellSize + Constants.CellHalf;
                    }
                    Pen pen = PenBrushFactory.GetPen(StarShip.RadiusColor);
                    g.DrawEllipse(pen, drawX - Surface.OffsetX - radius, drawY - Surface.OffsetY - radius, radius * 2, radius * 2);
                }

                //Draw the order lines
                if (StarShip.Order != null && StarShip.Order.LineColor != SCGColor.Transparent)
                {
                    Pen orderPen = PenBrushFactory.GetPen(StarShip.Order.LineColor);
                    int orderX1 = 0;
                    int orderX2 = 0;
                    int orderY1 = 0;
                    int orderY2 = 0;
                    if (StarShip.Order is PatrolOrder)
                    {
                        PatrolOrder po = StarShip.Order as PatrolOrder;
                        //render the patrol path
                        for (int i = 1; i < po.PatrolPoints.Count; i++)
                        {
                            orderX1 = (int)(po.PatrolPoints[i - 1].X * Constants.CellSize - Surface.OffsetX + Constants.CellHalf);
                            orderY1 = (int)(po.PatrolPoints[i - 1].Y * Constants.CellSize - Surface.OffsetY + Constants.CellHalf);
                            orderX2 = (int)(po.PatrolPoints[i].X * Constants.CellSize - Surface.OffsetX + Constants.CellHalf);
                            orderY2 = (int)(po.PatrolPoints[i].Y * Constants.CellSize - Surface.OffsetY + Constants.CellHalf);
                            g.DrawLine(orderPen, orderX1, orderY1, orderX2, orderY2);
                        }

                        //render the move to the next point along the path
                        orderX1 = (int)(X - Surface.OffsetX);
                        orderY1 = (int)(Y - Surface.OffsetY);
                        orderX2 = (int)(po.PatrolPoints[po.NextPoint].X * Constants.CellSize - Surface.OffsetX + Constants.CellHalf);
                        orderY2 = (int)(po.PatrolPoints[po.NextPoint].Y * Constants.CellSize - Surface.OffsetY + Constants.CellHalf);
                        for (int line = 0; line < 3; line++)
                            g.DrawLine(orderPen, orderX1 + SVGame.RNG.Next(5) - 2, orderY1 + SVGame.RNG.Next(5) - 2, orderX2, orderY2);
                    }
                    else
                    {
                        bool draw = true;
                        orderX1 = (int)(X - Surface.OffsetX);
                        orderY1 = (int)(Y - Surface.OffsetY);
                        if (StarShip.Order.OrderTargetsStarShip)
                        {
                            StarShip target = StarShip.Order.StarShipTargeted;
                            if (target.VisibleToCurrentPlayer)
                            {
                                StarShipSprite sss = (StarShipSprite)target.Tag;
                                orderX2 = (int)(sss.X - Surface.OffsetX);
                                orderY2 = (int)(sss.Y - Surface.OffsetY);
                            }
                            else
                                draw = false;
                        }
                        else
                        {
                            SCGPoint target = StarShip.Order.TargetPoint;
                            orderX2 = (int)(target.X * Constants.CellSize - Surface.OffsetX + Constants.CellHalf);
                            orderY2 = (int)(target.Y * Constants.CellSize - Surface.OffsetY + Constants.CellHalf);
                        }
                        if (draw)
                            for (int line = 0; line < 3; line++)
                                g.DrawLine(orderPen, orderX1 + SVGame.RNG.Next(5) - 2, orderY1 + SVGame.RNG.Next(5) - 2, orderX2, orderY2);
                    }

                    //Draw freight lines
                    if (StarShip.Order.OrderType == OrderType.Transport)
                    {
                        TransportOrder to = StarShip.Order as TransportOrder;
                        for (int n = 1; n < to.Sources.Count; n++)
                        {
                            SVMapObject svm1 = to.Sources[n - 1];
                            SVMapObject svm2 = to.Sources[n];
                            Sprite s1 = (Sprite)svm1.Tag;
                            Sprite s2 = (Sprite)svm2.Tag;
                            orderX1 = (int)(s1.X - Surface.OffsetX);
                            orderY1 = (int)(s1.Y - Surface.OffsetY);
                            orderX2 = (int)(s2.X - Surface.OffsetX);
                            orderY2 = (int)(s2.Y - Surface.OffsetY);
                            g.DrawLine(orderPen, orderX1, orderY1, orderX2, orderY2);
                        }
                    }
                }
            }

            //Draw CloakDetector arrows?
            if (StarShip.OwnedByCurrentPlayer && StarShip.ShipType == StarShipType.CloakDetector)
            {
                CloakDetector cd = StarShip as CloakDetector;
                if (cd.CloakArrows.Count > 0)
                {
                    int cx = (int)(X - Surface.OffsetX - 5);
                    int cy = (int)(Y - Surface.OffsetY - 5);
                    foreach (int direction in cd.CloakArrows)
                    {
                        int cax = cx;
                        int cay = cy;
                        switch (direction)
                        {
                            case 0:
                                cay -= 12;
                                break;
                            case 1:
                                cay -= 12;
                                cax += 12;
                                break;
                            case 2:
                                cax += 12;
                                break;
                            case 3:
                                cax += 12;
                                cay += 12;
                                break;
                            case 4:
                                cay += 12;
                                break;
                            case 5:
                                cay += 12;
                                cax -= 12;
                                break;
                            case 6:
                                cax -= 12;
                                break;
                            case 7:
                                cax -= 12;
                                cay -= 12;
                                break;
                        }
                        Bitmap cloakArrow = CloakArrowPieces[direction];
                        g.DrawImage(cloakArrow, cax, cay);
                    }
                }
            }
        }

        //private members
        private static Font ValuesFont = new Font("Arial", 7);
        private static Brush LabelBackgroundBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 0));
        private static List<Bitmap> CloakArrowPieces = new List<Bitmap>();
        private int _x1;
        private int _y1;
        private int _x2;
        private int _y2;

        //Draw rays from one sprite to another
        private void DrawRays(Graphics g, SVMapObject objFrom, SVMapObject objTo, Color colorFrom, Color colorTo)
        {
            Sprite s1 = (Sprite)objFrom.Tag;
            Sprite s2 = (Sprite)objTo.Tag;
            Pen rayPen = new Pen(Color.Black);
            for (int line = 0; line < 12; line++)
            {
                Color col = Sprite.ColorFromRange(colorFrom, colorTo);
                rayPen.Color = col;
                rayPen.Width = SVGame.RNG.Next(2) + 1;
                g.DrawLine(rayPen, s1.X - Surface.OffsetX, s1.Y - Surface.OffsetY, s2.X + SVGame.RNG.Next(30) - Surface.OffsetX - 15, s2.Y + SVGame.RNG.Next(30) - Surface.OffsetY - 15);
            }
        }

        //static constructor
        static StarShipSprite()
        {
            Bitmap cloakArrow = (Bitmap)Bitmap.FromFile("Images\\CloakArrow.bmp");
            int angle = 0;
            for (int n = 0; n < 8; n++)
            {
                Bitmap bmp = (Bitmap)ImageUtils.RotateImage(cloakArrow, angle);
                bmp.MakeTransparent(Color.Black);
                CloakArrowPieces.Add(bmp);
                angle += 45;
            }
        }

        //Draw a label with a black background
        private void DrawLabel(Graphics g, string text, Font font, Brush brush, float x, float y)
        {
            SizeF size = g.MeasureString(text, font);
            int width = (int)size.Width;
            int height = (int)size.Height;
            g.FillRectangle(LabelBackgroundBrush, x, y, width, height);
            g.DrawString(text, font, brush, x, y);
        }

        //The StarShip
        public StarShip StarShip { get; set; }
    }
}
